/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package FrontEnd;

import Client.IPAddress;
import SBLib.IPAddresses;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Handles requests between client and Replication Manger server.
 * @author BrandonNeal
 */
public class FEServerWorker extends Thread {
    
    private Socket clientSocket;
    private ObjectInputStream in;
    private ObjectOutputStream out;    
    
    //private Socket serverSocket = null;
    //private ObjectInputStream serverIn = null;
    //private ObjectOutputStream serverOut = null;    
    
    private Vector<Socket> RMSockets = null;
    private Vector<ObjectOutputStream> RMOutputs =null;
    private Vector<ObjectInputStream> RMInputs = null;
        
    final int TIMEOUT = 1000;
    
    public FEServerWorker(Socket clientSocket, Vector<Socket> rmSockets, Vector<ObjectInputStream> RMInputs, Vector<ObjectOutputStream> RMOutputs)
    {
        this.clientSocket = clientSocket;
        this.RMSockets = rmSockets;
        this.RMOutputs = RMOutputs;
        this.RMInputs = RMInputs;
        this.start();
        
    }
    
    @Override
    public void run()
    {
        
        Boolean connected=false;
        try 
        {
            
            this.in = new ObjectInputStream(this.clientSocket.getInputStream());
            this.out = new ObjectOutputStream(this.clientSocket.getOutputStream());
            while(true)
            {
                
                String fromClient = (String) this.in.readObject();
                System.out.println("Recieved:" + fromClient);
                connected = connected();
                
                synchronized(this.RMSockets)
                {
                    if(connected)
                        this.out.writeObject(this.HandleClientRequest(fromClient));
                    else
                        this.out.writeObject("CRASHED&&&");
                    //this.removeResetSockets();
                }
            }
            
        } 
        catch (ClassNotFoundException ex) 
        {
            Logger.getLogger(FEServerWorker.class.getName()).log(Level.SEVERE, null, ex);
        }
        catch (IOException ex)
        {
            Logger.getLogger(FEServerWorker.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        
    }
    /*private void removeResetSockets()
    {
        for(Socket s:FEServer.ToRemove)
        {
            this.RMSockets.remove(s);
        }
        FEServer.ToRemove.clear();
    }*/
    /**
     * CHANGE LATER
     * @param request
     * @return 
     */
    public String HandleClientRequest(String request)
    {
        
        ArrayList<FEMessageHandler> MessageHandlers = new ArrayList<FEMessageHandler>();
    
        for(int i=0; i<this.RMSockets.size(); i++)
        {
            FEMessageHandler fEMessageHandler = new FEMessageHandler(this.RMSockets.get(i),request, this.RMInputs.get(i), this.RMOutputs.get(i), RMSockets, RMInputs, RMOutputs);
            MessageHandlers.add(fEMessageHandler);
        }
           
        for(FEMessageHandler feMH:MessageHandlers)
        {
            try 
            {
                feMH.join(500);//will interrupt the thread of it doesn't respond in time.
            }
            catch (InterruptedException ex) 
            {
             Logger.getLogger(FEServerWorker.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return this.getResponse(MessageHandlers);
    }
    private String getResponse(ArrayList<FEMessageHandler> MessageHandlers)
    {
        HashMap<String, Integer> responses = new HashMap<String, Integer>();
        int currMax=0;
        int newCount=0;
        String mostOccuringString="";
        Boolean haveTie=false;
        for(FEMessageHandler feM:MessageHandlers)
        {
            //only interested in the non-Crashed responses, and ones where the responses didn't time out.
            if(!feM.Response.equals("CRASHED&&&") && !feM.Response.equals(""))
            {
                if(responses.containsKey(feM.Response))//if it's already in it.
                {
                    newCount = responses.get(feM.Response).intValue()+1;
                    responses.put(feM.Response, newCount);
                }
                else{ //if this is the initial insertion
                    newCount=1;
                    responses.put(feM.Response, newCount);
                }
                
                if(newCount>currMax) //if the current response now has the most occurences
                {
                    mostOccuringString = feM.Response;
                    haveTie=false;
                    currMax=newCount;
                }
                else if(newCount==currMax)
                    haveTie=true;
                
                if(mostOccuringString.equals(""))//if this is the 1st one
                {
                    mostOccuringString = feM.Response;
                    currMax=1;
                }
            }
        }
        
        return (mostOccuringString.equals("") || haveTie) ? "CRASHED&&&" : this.removeSocketsWithNonMajorityResponse_getMostOccurringResponse(mostOccuringString, MessageHandlers);
    }
    private String removeSocketsWithNonMajorityResponse_getMostOccurringResponse(String mostOccuringResponse, ArrayList<FEMessageHandler> MessageHandlers)
    {
        int i=0;
        for(FEMessageHandler feM:MessageHandlers)
        {
            if(!feM.Response.equals(mostOccuringResponse))
            {
                this.RMSockets.remove(this.RMSockets.get(i));
                this.RMInputs.remove(this.RMInputs.get(i));
                this.RMOutputs.remove(this.RMOutputs.get(i));
                i--;
            }
            i++;
        }
        return mostOccuringResponse;
    }
    /**
     * - Connects if there has been no previous connection, or, if there is a new best connection
     * @throws IOException 
     */
    public Boolean connected()
    {
        System.out.println("Next request will be sent to : "+this.RMSockets.size()+" RM servers.");
        return this.RMSockets.size()>0;
    }
    
}
